﻿using Sys.Core.Models.Dto;
using Sys.Core.Models.Queries;
using Sys.Core.Models.VO;
using Sys.Core.Providers;
using MiniFox.Components;
using MiniFox.Data.EntityFramework;
using MiniFox.Infrastructure;
using MiniFox.Platform;
using MiniFox.Platform.Applications.Models;
using System.Reflection;
using System;
using Microsoft.EntityFrameworkCore;

namespace Sys.Data
{
    [AutoResolve]
    public class RoleProvider : EntityDataProvider<PlatformDbContext>, IRoleProvider
    {
        private RoleVo MappingToVo(SysRole sysRole)
        {
            if (sysRole == null) return null;
            return new RoleVo
            {
                RoleID = sysRole.RoleId,
                RoleName = sysRole.RoleName,
                Description = sysRole.Description,
                CreateTime = sysRole.CreateTime,
                UpdateTime = sysRole.UpdateTime
            };
        }
        public ResultObject<IEnumerable<RoleVo>> Query(PagedQueryObject<RoleQuery> query)
        {
            var resultSet = this.PagedQueryExecute<SysRole>(query);
            return resultSet.GetResultObject(sysRole => new RoleVo
            {
                RoleID = sysRole.RoleId,
                RoleName = sysRole.RoleName,
                Description = sysRole.Description,
                CreateTime = sysRole.CreateTime,
                UpdateTime = sysRole.UpdateTime
            });
        }

        public bool IsUsedRoleName(string roleName, string roleId = null)
        {
            return string.IsNullOrEmpty(roleId) ? this.CurrentDbContext.SysRole.Any(u => u.RoleName == roleName) : this.CurrentDbContext.SysRole.Any(u => u.RoleName == roleName && u.RoleId != roleId);
        }

        public bool CreateRole(RoleDto role)
        {
            var sysRole = new SysRole();
            sysRole.RoleId = role.RoleID;
            sysRole.RoleName = role.RoleName;
            sysRole.Description = role.Description;
            sysRole.CreateTime = DateTime.Now;
            sysRole.UpdateTime = DateTime.Now;
            return this.Insert(sysRole) > 0;
        }

        public bool UpdateRole(RoleDto role)
        {
            var sysRole = this.CurrentDbContext.SysRole.SingleOrDefault(r => r.RoleId == role.RoleID);
            sysRole.RoleName = role.RoleName;
            sysRole.Description = role.Description;
            sysRole.UpdateTime = DateTime.Now;
            return this.Update(sysRole) > 0;
        }

        public bool DeleteRole(params string[] roles)
        {
            List<object> list = new List<object>();
            foreach(var roleId in roles)
            {
                //var roleModules = (from rm in this.CurrentDbContext.SysRoleModule where rm.RoleId == roleId select rm).ToArray();
                //list.AddRange(roleModules);
                //var roleUsers = (from ru in this.CurrentDbContext.SysUserRole where ru.RoleId == roleId select ru).ToArray();
                //list.AddRange(roleUsers);
                var role = this.CurrentDbContext.SysRole.SingleOrDefault(r => r.RoleId == roleId);
                list.Add(role);
            }

            return this.Delete(list.ToArray()) > 0;
        }

        #region role users
        public RoleVo GetRole(string roleId)
        {
            var role = this.CurrentDbContext.SysRole.SingleOrDefault(r => r.RoleId == roleId);
            return MappingToVo(role);
        }

        public bool AddUsers(string roleId, params string[] users)
        {
            List<SysUserRole> roleUsers = new List<SysUserRole>();
            foreach(var uesrId in users)
            {
                var userRole = new SysUserRole();
                userRole.UserId = uesrId;
                userRole.RoleId = roleId;
                roleUsers.Add(userRole);
            }
            return this.Insert(roleUsers.ToArray()) > 0;
        }

        public bool RemoveUsers(string roleId, params string[] users)
        {
            SysUserRole[] roleUsers = (from ru in this.CurrentDbContext.SysUserRole where ru.RoleId == roleId && users.Contains(ru.UserId) select ru).ToArray();
            return this.Delete(roleUsers) > 0;
        }

        public ResultObject<IEnumerable<UserVo>> InclusionUsers(string roleId, PagedQueryObject<UserQuery> request)
        {
            var roleUsers = (from u in this.CurrentDbContext.SysUser
                             join ru in this.CurrentDbContext.SysUserRole on u.UserId equals ru.UserId
                             where ru.RoleId == roleId
                             select u);
            var result = this.PagedQueryExecute(roleUsers, request);
            return result.GetResultObject(u => new UserVo
            {
                UserID = u.UserId,
                UpdateTime = u.UpdateTime,
                Cellphone = u.Cellphone,
                CreateTime = u.CreateTime,
                DisplayName = u.DisplayName,
                LoginName = u.LoginName,
                SecEmail = u.SecEmail,
                UserStatus = Enum.Parse<IdentityStatus>(u.UserStatus),
            });
        }

        public ResultObject<IEnumerable<UserVo>> ExclusionUsers(string roleId, PagedQueryObject<UserQuery> request)
        {
            var roleUsers = (from r in this.CurrentDbContext.SysUserRole where r.RoleId == roleId select r.UserId).ToArray();
            var users = (from u in this.CurrentDbContext.SysUser
                             where !roleUsers.Contains(u.UserId)
                             select u);
            var result = this.PagedQueryExecute(users, request);
            return result.GetResultObject(u => new UserVo
            {
                UserID = u.UserId,
                UpdateTime = u.UpdateTime,
                Cellphone = u.Cellphone,
                CreateTime = u.CreateTime,
                DisplayName = u.DisplayName,
                LoginName = u.LoginName,
                SecEmail = u.SecEmail,
                UserStatus = Enum.Parse<IdentityStatus>(u.UserStatus),
            });
        }
        #endregion

        #region role modules
        public bool AddModules(string roleId, params string[] modules)
        {
            List<BatchOperationItem> list = new List<BatchOperationItem>();
            foreach (var moduleId in modules)
            {
                var roleModule = new SysRoleModule();
                roleModule.ModuleId = moduleId;
                roleModule.RoleId = roleId;
                list.Add(new BatchOperationItem
                {
                    Entity = roleModule,
                    State = EntityState.Added
                });

                var moduleActions = (from ma in this.CurrentDbContext.SysModuleAction
                                     where ma.ModuleId == moduleId
                                     select new BatchOperationItem
                                     {
                                         Entity = new SysRoleModuleAction
                                         {
                                             RoleId = roleId,
                                             ModuleId = moduleId,
                                             ActionId = ma.ActionId
                                         },
                                         State = EntityState.Added
                                     }).ToArray();
                list.AddRange(moduleActions);
            }
            return this.CommitBatch(list) > 0;
        }

        public bool RemoveModules(string roleId, params string[] modules)
        {
            SysRoleModule[] roleModules = (from rm in this.CurrentDbContext.SysRoleModule where rm.RoleId == roleId && modules.Contains(rm.ModuleId) select rm).ToArray();
            return this.Delete(roleModules) > 0;
        }

        public ResultObject<IEnumerable<ModuleVo>> InclusionModules(string roleId, PagedQueryObject<ModuleQuery> request)
        {
            var roleModules = (from m in this.CurrentDbContext.SysModule
                               join a in this.CurrentDbContext.SysApp on m.AppId equals a.AppId
                               join rm in this.CurrentDbContext.SysRoleModule on m.ModuleId equals rm.ModuleId
                               where rm.RoleId == roleId
                               select new
                               {
                                   m.ModuleId,
                                   m.ModuleName,
                                   m.IsPublic,
                                   ApplicationID = m.AppId,
                                   m.ModuleUri,
                                   m.QueryString,
                                   m.CreateTime,
                                   m.UpdateTime,
                                   m.Enabled,
                                   m.Icon,
                                   m.OpenMode,
                                   a.Ssl,
                                   a.AppName,
                                   a.Host,
                                   a.Port,
                                   a.Path
                               });
            var result = this.PagedQueryExecute(roleModules, request);
            return result.GetResultObject(m => new ModuleVo
            {
                ModuleID = m.ModuleId,
                ModuleName = m.ModuleName,
                ApplicationID = m.ApplicationID,
                ApplicationDomain = m.Host,
                ApplicationPath = m.Path,
                Icon = m.Icon,
                Enabled = m.Enabled,
                CreateTime = m.CreateTime,
                UpdateTime = m.UpdateTime,
                IsPublic = m.IsPublic,
                OpenMode = Enum.Parse<OpenMode>(m.OpenMode),
                PortNumber = m.Port,
                QueryString = m.QueryString,
                RequireSSL = m.Ssl,
                Uri = m.ModuleUri,
                Actions = (from ma in this.CurrentDbContext.SysModuleAction
                           where ma.ModuleId == m.ModuleId
                           select new ModuleActionVo
                           {
                               ActionID = ma.ActionId,
                               ActionName = ma.ActionName,
                               Uri = ma.ActionUri,
                               ApplicationDomain = m.Host,
                               ApplicationPath = m.Path,
                               PortNumber = m.Port,
                               RequireSSL = m.Ssl,
                               CreateTime = ma.CreateTime,
                               UpdateTime = ma.UpdateTime
                           }).ToArray()
            });
        }

        public ResultObject<IEnumerable<ModuleVo>> ExclusionModules(string roleId, PagedQueryObject<ModuleQuery> request)
        {
            var roleModules = (from rm in this.CurrentDbContext.SysRoleModule where rm.RoleId == roleId select rm.ModuleId).ToArray();
            var modules = (from m in this.CurrentDbContext.SysModule
                               join a in this.CurrentDbContext.SysApp on m.AppId equals a.AppId
                               where !roleModules.Contains(m.ModuleId)
                               select new
                               {
                                   m.ModuleId,
                                   m.ModuleName,
                                   m.IsPublic,
                                   ApplicationID = m.AppId,
                                   m.ModuleUri,
                                   m.QueryString,
                                   m.CreateTime,
                                   m.UpdateTime,
                                   m.Enabled,
                                   m.Icon,
                                   m.OpenMode,
                                   a.Ssl,
                                   a.AppName,
                                   a.Host,
                                   a.Port,
                                   a.Path
                               });
            var result = this.PagedQueryExecute(modules, request);
            return result.GetResultObject(m => new ModuleVo
            {
                ModuleID = m.ModuleId,
                ModuleName = m.ModuleName,
                ApplicationID = m.ApplicationID,
                ApplicationDomain = m.Host,
                ApplicationPath = m.Path,
                Icon = m.Icon,
                Enabled = m.Enabled,
                CreateTime = m.CreateTime,
                UpdateTime = m.UpdateTime,
                IsPublic = m.IsPublic,
                OpenMode = Enum.Parse<OpenMode>(m.OpenMode),
                PortNumber = m.Port,
                QueryString = m.QueryString,
                RequireSSL = m.Ssl,
                Uri = m.ModuleUri,
                Actions = (from ma in this.CurrentDbContext.SysModuleAction
                           where ma.ModuleId == m.ModuleId
                           select new ModuleActionVo
                           {
                               ActionID = ma.ActionId,
                               ActionName = ma.ActionName,
                               Uri = ma.ActionUri,
                               ApplicationDomain = m.Host,
                               ApplicationPath = m.Path,
                               PortNumber = m.Port,
                               RequireSSL = m.Ssl,
                               CreateTime = ma.CreateTime,
                               UpdateTime = ma.UpdateTime
                           }).ToArray()
            });
        }
        #endregion
    }
}
